home *** CD-ROM | disk | FTP | other *** search
/ Aminet 28 / Aminet 28 (1998)(GTI - Schatztruhe)[!][Dec 1998].iso / Aminet / dev / c / qtools0.2-src.lha / src / libqtools / mdl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-07-18  |  23.4 KB  |  1,029 lines

  1. #define    LIBQTOOLS_CORE
  2. #define    LIBQBUILD_CORE
  3. #include "../include/libqtools.h"
  4. #include "../include/libqbuild.h"
  5.  
  6. /*
  7.  * modelgen.c: generates a .mdl file from a base triangle file (.tri), a
  8.  * texture containing front and back skins (.lbm), and a series of frame
  9.  * triangle files (.tri). Result is stored in
  10.  * /raid/quake/models/<scriptname>.mdl.
  11.  */
  12.  
  13. struct mdlheader model;
  14.  
  15. char file1[1024];
  16. char skinname[1024];
  17. char qbasename[1024];
  18. float scale, scale_up = 1.0;
  19. vec3_t mins, maxs;
  20. vec3_t framesmins, framesmaxs;
  21. vec3_t adjust;
  22.  
  23. /*
  24.  * base frame info
  25.  */
  26.  
  27. char cdpartial[256];
  28. char cddir[256];
  29.  
  30. bool cdset;
  31. int degeneratetris;
  32. int firstframe = 1;
  33. float totsize, averagesize;
  34.  
  35. #define NUMVERTEXNORMALS    162
  36.  
  37. float avertexnormals[NUMVERTEXNORMALS][3] =
  38. {
  39. #include "anorms.h"
  40. };
  41.  
  42. char outname[1024];
  43.  
  44. /*============================================================================ */
  45.  
  46. void ClearModel(void)
  47. {
  48.   __bzero(&model, sizeof(model));
  49.   model.synctype = ST_RAND;                    /* default */
  50.  
  51.   bspMem->numframes = bspMem->numskins = 0;
  52.  
  53.   scale = 0;
  54.   scale_up = 1.0;
  55.  
  56.   VectorCopy(vec3_origin, adjust);
  57.   VectorCopy(vec3_origin, mins);
  58.   VectorCopy(vec3_origin, maxs);
  59.   VectorCopy(vec3_origin, framesmins);
  60.   VectorCopy(vec3_origin, framesmaxs);
  61.  
  62.   degeneratetris = 0;
  63.   cdset = false;
  64.   firstframe = 1;
  65.   totsize = 0.0;
  66. }
  67.  
  68. /*
  69.  * ============
  70.  * WriteFrame
  71.  * ============
  72.  */
  73. void WriteFrame(HANDLE modelouthandle, int framenum)
  74. {
  75.   int j, k;
  76.   struct trivert *pframe;
  77.   struct daliasframe aframe;
  78.   float v;
  79.  
  80.   pframe = bspMem->verts[framenum];
  81.  
  82.   strcpy(aframe.name, bspMem->frames[framenum].name);
  83.  
  84.   for (j = 0; j < 3; j++) {
  85.     aframe.bboxmin.v[j] = 255;
  86.     aframe.bboxmax.v[j] = 0;
  87.   }
  88.  
  89.   for (j = 0; j < model.numverts; j++) {
  90.     /* all of these are byte values, so no need to deal with endianness */
  91.     bspMem->tarray[j].lightnormalindex = pframe[j].lightnormalindex;
  92.  
  93.     if (bspMem->tarray[j].lightnormalindex > NUMVERTEXNORMALS)
  94.       Error("invalid lightnormalindex %d\n", bspMem->tarray[j].lightnormalindex);
  95.  
  96.     for (k = 0; k < 3; k++) {
  97.       /* scale to byte values & min/max check */
  98.       v = (pframe[j].v[k] - model.scale_origin[k]) / model.scale[k];
  99.  
  100.       bspMem->tarray[j].v[k] = v;
  101.  
  102.       if (bspMem->tarray[j].v[k] < aframe.bboxmin.v[k]) {
  103.     aframe.bboxmin.v[k] = bspMem->tarray[j].v[k];
  104.       }
  105.       if (bspMem->tarray[j].v[k] > aframe.bboxmax.v[k]) {
  106.     aframe.bboxmax.v[k] = bspMem->tarray[j].v[k];
  107.       }
  108.  
  109.     }
  110.   }
  111.  
  112.   __write(modelouthandle, &aframe, sizeof(aframe));
  113.   __write(modelouthandle, &bspMem->tarray[0], model.numverts * sizeof(bspMem->tarray[0]));
  114. }
  115.  
  116. /*
  117.  * ============
  118.  * WriteGroupBBox
  119.  * ============
  120.  */
  121. void WriteGroupBBox(HANDLE modelouthandle, int numframes, int curframe)
  122. {
  123.   int i, j, k;
  124.   struct daliasgroup dagroup;
  125.   struct trivert *pframe;
  126.  
  127.   dagroup.numframes = LittleLong(numframes);
  128.  
  129.   for (i = 0; i < 3; i++) {
  130.     dagroup.bboxmin.v[i] = 255;
  131.     dagroup.bboxmax.v[i] = 0;
  132.   }
  133.  
  134.   for (i = 0; i < numframes; i++) {
  135.     pframe = (struct trivert *)bspMem->frames[curframe].pdata;
  136.  
  137.     for (j = 0; j < model.numverts; j++) {
  138.       for (k = 0; k < 3; k++) {
  139.     /* scale to byte values & min/max check */
  140.     bspMem->tarray[j].v[k] = (pframe[j].v[k] - model.scale_origin[k]) /
  141.       model.scale[k];
  142.     if (bspMem->tarray[j].v[k] < dagroup.bboxmin.v[k])
  143.       dagroup.bboxmin.v[k] = bspMem->tarray[j].v[k];
  144.     if (bspMem->tarray[j].v[k] > dagroup.bboxmax.v[k])
  145.       dagroup.bboxmax.v[k] = bspMem->tarray[j].v[k];
  146.       }
  147.     }
  148.  
  149.     curframe++;
  150.   }
  151.  
  152.   __write(modelouthandle, &dagroup, sizeof(dagroup));
  153. }
  154.  
  155. /*
  156.  * ============
  157.  * WriteModel
  158.  * ============
  159.  */
  160. bool WriteModelFile(HANDLE modelouthandle)
  161. {
  162.   int i, curframe, curskin;
  163.   float dist[3];
  164.   struct mdlheader modeltemp;
  165.  
  166.   /* Calculate the bounding box for this model */
  167.   for (i = 0; i < 3; i++) {
  168.     mprintf("framesmins[%d]: %f, framesmaxs[%d]: %f\n", i, framesmins[i], i, framesmaxs[i]);
  169.     if (fabs(framesmins[i]) > fabs(framesmaxs[i]))
  170.       dist[i] = framesmins[i];
  171.     else
  172.       dist[i] = framesmaxs[i];
  173.  
  174.     model.scale[i] = (framesmaxs[i] - framesmins[i]) / 255.9;
  175.     model.scale_origin[i] = framesmins[i];
  176.   }
  177.  
  178.   model.boundingradius = VectorLength(dist);
  179.  
  180.   /*
  181.    * write out the model header
  182.    */
  183.   modeltemp.ident = LittleLong(MAGIC_MDL_Q1);
  184.   modeltemp.version = LittleLong(MDL_VERSION);
  185.   modeltemp.boundingradius = LittleFloat(model.boundingradius);
  186.  
  187.   for (i = 0; i < 3; i++) {
  188.     modeltemp.scale[i] = LittleFloat(model.scale[i]);
  189.     modeltemp.scale_origin[i] = LittleFloat(model.scale_origin[i]);
  190.     modeltemp.eyeposition[i] = LittleFloat(model.eyeposition[i] + adjust[i]);
  191.   }
  192.  
  193.   modeltemp.flags = LittleLong(model.flags);
  194.   modeltemp.numskins = LittleLong(model.numskins);
  195.   modeltemp.skinwidth = LittleLong(model.skinwidth);
  196.   modeltemp.skinheight = LittleLong(model.skinheight);
  197.   modeltemp.numverts = LittleLong(model.numverts);
  198.   modeltemp.numtris = LittleLong(model.numtris - degeneratetris);
  199.   modeltemp.numframes = LittleLong(model.numframes);
  200.   modeltemp.synctype = LittleFloat(model.synctype);
  201.   averagesize = totsize / model.numtris;
  202.   modeltemp.size = LittleFloat(averagesize);
  203.  
  204.   __write(modelouthandle, &modeltemp, sizeof(model));
  205.  
  206.   /*
  207.    * write out the skins
  208.    */
  209.   curskin = 0;
  210.  
  211.   for (i = 0; i < model.numskins; i++) {
  212.     __write(modelouthandle, &bspMem->skins[curskin].type, sizeof(bspMem->skins[curskin].type));
  213.     __write(modelouthandle, bspMem->skins[curskin].pdata, model.skinwidth * model.skinheight);
  214.     curskin++;
  215.   }
  216.  
  217.   /*
  218.    * write out the base model (the s & t coordinates for the vertices)
  219.    */
  220.   for (i = 0; i < model.numverts; i++) {
  221.     if (bspMem->stverts[i].onseam == 3) {
  222.       bspMem->stverts[i].onseam = LittleLong(MDL_ONSEAM);
  223.     }
  224.     else {
  225.       bspMem->stverts[i].onseam = LittleLong(0);
  226.     }
  227.  
  228.     bspMem->stverts[i].s = LittleLong(bspMem->stverts[i].s);
  229.     bspMem->stverts[i].t = LittleLong(bspMem->stverts[i].t);
  230.   }
  231.  
  232.   __write(modelouthandle, bspMem->stverts, model.numverts * sizeof(bspMem->stverts[0]));
  233.  
  234.   /*
  235.    * write out the triangles
  236.    */
  237.   for (i = 0; i < model.numtris; i++) {
  238.     int j;
  239.     struct dtriangle tri;
  240.  
  241.     if (!bspMem->degenerate[i]) {
  242.       tri.facesfront = LittleLong(bspMem->triangles[i].facesfront);
  243.  
  244.       for (j = 0; j < 3; j++) {
  245.     tri.vertindex[j] = LittleLong(bspMem->triangles[i].vertindex[j]);
  246.       }
  247.       __write(modelouthandle, &tri, sizeof(tri));
  248.     }
  249.   }
  250.  
  251.   /*
  252.    * write out the frames
  253.    */
  254.   curframe = 0;
  255.  
  256.   for (i = 0; i < model.numframes; i++) {
  257.     __write(modelouthandle, &bspMem->frames[curframe].type, sizeof(bspMem->frames[curframe].type));
  258.  
  259.     if (bspMem->frames[curframe].type == MDL_SINGLE) {
  260.       /* single (non-grouped) frame */
  261.       WriteFrame(modelouthandle, curframe);
  262.       curframe++;
  263.     }
  264.     else {
  265.       int j, numframes, groupframe;
  266.       float totinterval;
  267.  
  268.       groupframe = curframe;
  269.       curframe++;
  270.       numframes = bspMem->frames[groupframe].numgroupframes;
  271.  
  272.       /* set and write the group header */
  273.       WriteGroupBBox(modelouthandle, numframes, curframe);
  274.  
  275.       /* write the interval array */
  276.       totinterval = 0.0;
  277.  
  278.       for (j = 0; j < numframes; j++) {
  279.     daliasinterval_t temp;
  280.  
  281.     totinterval += bspMem->frames[groupframe + 1 + j].interval;
  282.     temp.interval = LittleFloat(totinterval);
  283.  
  284.     __write(modelouthandle, &temp, sizeof(temp));
  285.       }
  286.  
  287.       for (j = 0; j < numframes; j++) {
  288.     WriteFrame(modelouthandle, curframe);
  289.     curframe++;
  290.       }
  291.     }
  292.   }
  293.   
  294.   return TRUE;
  295. }
  296.  
  297. /*
  298.  * ===============
  299.  * WriteModel
  300.  * ===============
  301.  */
  302. bool WriteModel(void)
  303. {
  304.   HANDLE modelouthandle;
  305.   bool success = FALSE;
  306.  
  307.   /* write the model output file */
  308.   if (!bspMem->numframes) {
  309.     eprintf("no frames grabbed, no file generated\n");
  310.   }
  311.   else {
  312.     if (bspMem->numskins) {
  313.     ReplaceExt(outname, "mdl");
  314.  
  315.     mprintf("---------------------\n");
  316.     mprintf("writing %s:\n", outname);
  317.     if((modelouthandle = __open(outname, H_WRITE_BINARY)) > 0) {
  318.       if(WriteModelFile(modelouthandle)) {
  319.         mprintf("%4d frame(s)\n", model.numframes);
  320.         mprintf("%4d ungrouped frame(s), including group headers\n", bspMem->numframes);
  321.         mprintf("%4d skin(s)\n", model.numskins);
  322.         mprintf("%4d degenerate triangles(s) removed\n", degeneratetris);
  323.         mprintf("%4d triangles emitted\n", model.numtris - degeneratetris);
  324.         mprintf("pixels per triangle %f\n", averagesize);
  325.  
  326.         mprintf("file size: %d\n", (int)ftell(modelouthandle));
  327.         mprintf("---------------------\n");
  328.         success = TRUE;
  329.       }
  330.       else
  331.         eprintf("failed to save model to %s\n", outname);
  332.       __close(modelouthandle);
  333.  
  334.       ClearModel();
  335.     }
  336.     else
  337.       eprintf("failed to open file %s\n", outname);
  338.     }
  339.     else
  340.       eprintf("frames with no skins\n");
  341.   }
  342.   
  343.   return success;
  344. }
  345.  
  346. /*
  347.  * ============
  348.  * SetSkinValues
  349.  * 
  350.  * Called for the base frame
  351.  * ============
  352.  */
  353. void SetSkinValues(void)
  354. {
  355.   int i;
  356.   float v;
  357.   int width, height, iwidth, iheight, skinwidth;
  358.   float basex, basey;
  359.  
  360.   for (i = 0; i < 3; i++) {
  361.     mins[i] = 9999999;
  362.     maxs[i] = -9999999;
  363.   }
  364.  
  365.   for (i = 0; i < model.numverts; i++) {
  366.     int j;
  367.  
  368.     bspMem->stverts[i].onseam = 0;
  369.  
  370.     for (j = 0; j < 3; j++) {
  371.       v = bspMem->baseverts[i][j];
  372.       if (v < mins[j])
  373.     mins[j] = v;
  374.       if (v > maxs[j])
  375.     maxs[j] = v;
  376.     }
  377.   }
  378.  
  379.   for (i = 0; i < 3; i++) {
  380.     mins[i] = floor(mins[i]);
  381.     maxs[i] = ceil(maxs[i]);
  382.   }
  383.  
  384.   width = maxs[0] - mins[0];
  385.   height = maxs[2] - mins[2];
  386.  
  387.   mprintf("width: %i  height: %i\n", width, height);
  388.  
  389.   scale = 8;
  390.   if (width * scale >= 150)
  391.     scale = 150.0 / width;
  392.   if (height * scale >= 190)
  393.     scale = 190.0 / height;
  394.   iwidth = ceil(width * scale) + 4;
  395.   iheight = ceil(height * scale) + 4;
  396.  
  397.   mprintf("scale: %f\n", scale);
  398.   mprintf("iwidth: %i  iheight: %i\n", iwidth, iheight);
  399.  
  400.   /* determine which side of each triangle to map the texture to */
  401.   for (i = 0; i < model.numtris; i++) {
  402.     int j;
  403.     vec3_t vtemp1, vtemp2, normal;
  404.  
  405.     VectorSubtract(bspMem->baseverts[bspMem->triangles[i].vertindex[0]], bspMem->baseverts[bspMem->triangles[i].vertindex[1]], vtemp1);
  406.     VectorSubtract(bspMem->baseverts[bspMem->triangles[i].vertindex[2]], bspMem->baseverts[bspMem->triangles[i].vertindex[1]], vtemp2);
  407.     CrossProduct(vtemp1, vtemp2, normal);
  408.  
  409.     if (normal[1] > 0) {
  410.       basex = iwidth + 2;
  411.       bspMem->triangles[i].facesfront = 0;
  412.     }
  413.     else {
  414.       basex = 2;
  415.       bspMem->triangles[i].facesfront = 1;
  416.     }
  417.     basey = 2;
  418.  
  419.     for (j = 0; j < 3; j++) {
  420.       float *pbasevert;
  421.       struct stvert *pstvert;
  422.  
  423.       pbasevert = bspMem->baseverts[bspMem->triangles[i].vertindex[j]];
  424.       pstvert = &bspMem->stverts[bspMem->triangles[i].vertindex[j]];
  425.  
  426.       if (bspMem->triangles[i].facesfront) {
  427.     pstvert->onseam |= 1;
  428.       }
  429.       else {
  430.     pstvert->onseam |= 2;
  431.       }
  432.  
  433.       if ((bspMem->triangles[i].facesfront) || ((pstvert->onseam & 1) == 0)) {
  434.     /* we want the front s value for seam vertices */
  435.     pstvert->s = rint((pbasevert[0] - mins[0]) * scale + basex);
  436.     pstvert->t = rint((maxs[2] - pbasevert[2]) * scale + basey);
  437.       }
  438.     }
  439.   }
  440.  
  441.   /*
  442.    * make the width a multiple of 4; some hardware requires this, and it ensures
  443.    * dword alignment for each scan
  444.    */
  445.   skinwidth = iwidth * 2;
  446.   model.skinwidth = (skinwidth + 3) & ~3;
  447.   model.skinheight = iheight;
  448.  
  449.   mprintf("skin width: %i (unpadded width %i)  skin height: %i\n",
  450.       model.skinwidth, skinwidth, model.skinheight);
  451. }
  452.  
  453. /*
  454.  * =================
  455.  * Cmd_Base
  456.  * =================
  457.  */
  458. void Cmd_Base(void)
  459. {
  460.   struct triangle *ptri;
  461.   int i, j, k;
  462.   int time1;
  463.  
  464.   GetToken(false);
  465.   strcpy(qbasename, token);
  466.   sprintf(file1, "%s/%s.tri", cdpartial, token);
  467.  
  468.   ExpandPathAndArchive(file1);
  469.  
  470.   sprintf(file1, "%s/%s.tri", cddir, token);
  471.   time1 = FileTime(file1);
  472.   if (time1 == -1)
  473.     Error("%s doesn't exist", file1);
  474.  
  475.   /* load the base triangles */
  476.   LoadTriangleList(file1, &ptri, &model.numtris);
  477.   mprintf("NUMBER OF TRIANGLES (including degenerate triangles): %d\n", model.numtris);
  478.  
  479.   /*
  480.    * run through all the base triangles, storing each unique vertex in the
  481.    * base vertex list and setting the indirect triangles to point to the base
  482.    * vertices
  483.    */
  484.   for (i = 0; i < model.numtris; i++) {
  485.     if (VectorCompare(ptri[i].verts[0], ptri[i].verts[1]) ||
  486.     VectorCompare(ptri[i].verts[1], ptri[i].verts[2]) ||
  487.     VectorCompare(ptri[i].verts[2], ptri[i].verts[0])) {
  488.       degeneratetris++;
  489.       bspMem->degenerate[i] = 1;
  490.     }
  491.     else {
  492.       bspMem->degenerate[i] = 0;
  493.     }
  494.  
  495.     for (j = 0; j < 3; j++) {
  496.       for (k = 0; k < model.numverts; k++)
  497.     if (VectorCompare(ptri[i].verts[j], bspMem->baseverts[k]))
  498.       break;                        /* this vertex is already in the base vertex list */
  499.  
  500.       if (k == model.numverts) {
  501.     /* new vertex */
  502.     VectorCopy(ptri[i].verts[j], bspMem->baseverts[model.numverts]);
  503.     model.numverts++;
  504.       }
  505.  
  506.       bspMem->triangles[i].vertindex[j] = k;
  507.     }
  508.   }
  509.  
  510.   mprintf("NUMBER OF VERTEXES: %i\n", model.numverts);
  511.  
  512.   /* calculate s & t for each vertex, and set the skin width and height */
  513.   SetSkinValues();
  514. }
  515.  
  516. /*
  517.  * ===============
  518.  * Cmd_Skin
  519.  * ===============
  520.  */
  521. void Cmd_Skin(void)
  522. {
  523.   struct palpic *pskinbitmap;
  524.   byte *ptemp1, *ptemp2;
  525.   int i;
  526.   int time1;
  527.  
  528.   GetToken(false);
  529.   strcpy(skinname, token);
  530.   sprintf(file1, "%s/%s.lbm", cdpartial, token);
  531.  
  532.   ExpandPathAndArchive(file1);
  533.  
  534.   sprintf(file1, "%s/%s.lbm", cddir, token);
  535.   time1 = FileTime(file1);
  536.   if (time1 == -1)
  537.     Error("%s not found", file1);
  538.  
  539.   if (TokenAvailable()) {
  540.     GetToken(false);
  541.     bspMem->skins[bspMem->numskins].interval = atof(token);
  542.     if (bspMem->skins[bspMem->numskins].interval <= 0.0)
  543.       Error("Non-positive interval");
  544.   }
  545.   else {
  546.     bspMem->skins[bspMem->numskins].interval = 0.1;
  547.   }
  548.  
  549.   /* load in the skin file */
  550.   pskinbitmap = GetImage(file1, 0, -model.skinwidth, -model.skinheight);
  551.  
  552.   /*
  553.    * now copy the part of the texture we care about, since LBMs are always
  554.    * loaded as 320x200 bitmaps
  555.    */
  556.   bspMem->skins[bspMem->numskins].pdata = tmalloc(model.skinwidth * model.skinheight);
  557.  
  558.   if (!bspMem->skins[bspMem->numskins].pdata)
  559.     Error("couldn't get memory for skin texture");
  560.  
  561.   ptemp1 = bspMem->skins[bspMem->numskins].pdata;
  562.   ptemp2 = pskinbitmap;
  563.  
  564.   for (i = 0; i < model.skinheight; i++) {
  565.     __memcpy(ptemp1, ptemp2, model.skinwidth);
  566.     ptemp1 += model.skinwidth;
  567.     ptemp2 += 320;
  568.   }
  569.  
  570.   if (bspMem->numskins >= bspMem->max_numskins)
  571.     EpandClusters(bspMem, MODEL_SKINS);
  572.   bspMem->numskins++;
  573. }
  574.  
  575. /*
  576.  * ===============
  577.  * GrabFrame
  578.  * ===============
  579.  */
  580. void GrabFrame(char *frame, int isgroup)
  581. {
  582.   struct triangle *ptri;
  583.   int i, j;
  584.   struct trivert *ptrivert;
  585.   int numtris;
  586.   int time1;
  587.  
  588.   sprintf(file1, "%s/%s.tri", cdpartial, frame);
  589.   ExpandPathAndArchive(file1);
  590.  
  591.   sprintf(file1, "%s/%s.tri", cddir, frame);
  592.   time1 = FileTime(file1);
  593.   if (time1 == -1)
  594.     Error("%s does not exist", file1);
  595.  
  596.   mprintf("grabbing %s\n", file1);
  597.   bspMem->frames[bspMem->numframes].interval = 0.1;
  598.   strcpy(bspMem->frames[bspMem->numframes].name, frame);
  599.  
  600.   /* load the frame */
  601.   LoadTriangleList(file1, &ptri, &numtris);
  602.  
  603.   if (numtris != model.numtris)
  604.     Error("number of triangles doesn't match\n");
  605.  
  606.   /* set the intervals */
  607.   if (isgroup && TokenAvailable()) {
  608.     GetToken(false);
  609.     bspMem->frames[bspMem->numframes].interval = atof(token);
  610.     if (bspMem->frames[bspMem->numframes].interval <= 0.0)
  611.       Error("Non-positive interval %s %f", token, bspMem->frames[bspMem->numframes].interval);
  612.   }
  613.   else {
  614.     bspMem->frames[bspMem->numframes].interval = 0.1;
  615.   }
  616.  
  617.   /* allocate storage for the frame's vertices */
  618.   ptrivert = bspMem->verts[bspMem->numframes];
  619.  
  620.   bspMem->frames[bspMem->numframes].pdata = ptrivert;
  621.   bspMem->frames[bspMem->numframes].type = MDL_SINGLE;
  622.  
  623.   for (i = 0; i < model.numverts; i++) {
  624.     bspMem->vnorms[i].numnormals = 0;
  625.   }
  626.  
  627.   /*
  628.    * store the frame's vertices in the same order as the base. This assumes the
  629.    * triangles and vertices in this frame are in exactly the same order as in the
  630.    * base
  631.    */
  632.   for (i = 0; i < numtris; i++) {
  633.     vec3_t vtemp1, vtemp2, normal;
  634.     float ftemp;
  635.  
  636.     if (bspMem->degenerate[i])
  637.       continue;
  638.  
  639.     if (firstframe) {
  640.       VectorSubtract(ptri[i].verts[0], ptri[i].verts[1], vtemp1);
  641.       VectorSubtract(ptri[i].verts[2], ptri[i].verts[1], vtemp2);
  642.       VectorScale(vtemp1, scale_up, vtemp1);
  643.       VectorScale(vtemp2, scale_up, vtemp2);
  644.       CrossProduct(vtemp1, vtemp2, normal);
  645.  
  646.       totsize += VectorLength(normal) / 2.0;
  647.     }
  648.  
  649.     VectorSubtract(ptri[i].verts[0], ptri[i].verts[1], vtemp1);
  650.     VectorSubtract(ptri[i].verts[2], ptri[i].verts[1], vtemp2);
  651.     CrossProduct(vtemp1, vtemp2, normal);
  652.  
  653.     VectorNormalize(normal);
  654.  
  655.     /* rotate the normal so the model faces down the positive x axis */
  656.     ftemp = normal[0];
  657.     normal[0] = -normal[1];
  658.     normal[1] = ftemp;
  659.  
  660.     for (j = 0; j < 3; j++) {
  661.       int k;
  662.       int vertindex;
  663.  
  664.       vertindex = bspMem->triangles[i].vertindex[j];
  665.  
  666.       /*
  667.        * rotate the vertices so the model faces down the positive x axis
  668.        * also adjust the vertices to the desired origin
  669.        */
  670.       ptrivert[vertindex].v[0] = ((-ptri[i].verts[j][1]) * scale_up) + adjust[0];
  671.       ptrivert[vertindex].v[1] = (ptri[i].verts[j][0] * scale_up) + adjust[1];
  672.       ptrivert[vertindex].v[2] = (ptri[i].verts[j][2] * scale_up) + adjust[2];
  673.  
  674.       for (k = 0; k < 3; k++) {
  675.     if (ptrivert[vertindex].v[k] < framesmins[k])
  676.       framesmins[k] = ptrivert[vertindex].v[k];
  677.  
  678.     if (ptrivert[vertindex].v[k] > framesmaxs[k])
  679.       framesmaxs[k] = ptrivert[vertindex].v[k];
  680.       }
  681.  
  682.       VectorCopy(normal, bspMem->vnorms[vertindex].normals[bspMem->vnorms[vertindex].numnormals]);
  683.       bspMem->vnorms[vertindex].numnormals++;
  684.     }
  685.   }
  686.  
  687.   /*
  688.    * calculate the vertex normals, match them to the template list, and store the
  689.    * index of the best match
  690.    */
  691.   for (i = 0; i < model.numverts; i++) {
  692.     int j;
  693.     vec3_t v;
  694.     float maxdot;
  695.     int maxdotindex;
  696.  
  697.     if (bspMem->vnorms[i].numnormals > 0) {
  698.       for (j = 0; j < 3; j++) {
  699.     int k;
  700.  
  701.     v[j] = 0;
  702.  
  703.     for (k = 0; k < bspMem->vnorms[i].numnormals; k++) {
  704.       v[j] += bspMem->vnorms[i].normals[k][j];
  705.     }
  706.  
  707.     v[j] /= bspMem->vnorms[i].numnormals;
  708.       }
  709.     }
  710.     else {
  711.       Error("Vertex with no non-degenerate triangles attached");
  712.     }
  713.  
  714.     VectorNormalize(v);
  715.  
  716.     maxdot = -999999.0;
  717.     maxdotindex = -1;
  718.  
  719.     for (j = 0; j < NUMVERTEXNORMALS; j++) {
  720.       float dot;
  721.  
  722.       dot = DotProduct(v, avertexnormals[j]);
  723.       if (dot > maxdot) {
  724.     maxdot = dot;
  725.     maxdotindex = j;
  726.       }
  727.     }
  728.  
  729.     ptrivert[i].lightnormalindex = maxdotindex;
  730.   }
  731.  
  732.   if (bspMem->numframes >= bspMem->max_numframes)
  733.     EpandClusters(bspMem, MODEL_FRAMES);
  734.   bspMem->numframes++;
  735.  
  736.   tfree(ptri);
  737.   firstframe = 0;
  738. }
  739.  
  740. /*
  741.  * ===============
  742.  * Cmd_Frame    
  743.  * ===============
  744.  */
  745. void Cmd_Frame(int isgroup)
  746. {
  747.   while (TokenAvailable()) {
  748.     GetToken(false);
  749.     GrabFrame(token, isgroup);
  750.  
  751.     if (!isgroup)
  752.       model.numframes++;
  753.   }
  754. }
  755.  
  756. /*
  757.  * ===============
  758.  * Cmd_SkinGroupStart   
  759.  * ===============
  760.  */
  761. void Cmd_SkinGroupStart(void)
  762. {
  763.   int groupskin;
  764.  
  765.   if (bspMem->numskins >= bspMem->max_numskins)
  766.     EpandClusters(bspMem, MODEL_SKINS);
  767.   groupskin = bspMem->numskins++;
  768.  
  769.   bspMem->skins[groupskin].type = MDL_SKIN_GROUP;
  770.   bspMem->skins[groupskin].numgroupskins = 0;
  771.  
  772.   while (1) {
  773.     GetToken(true);
  774.     if (endofscript)
  775.       Error("End of file during group");
  776.  
  777.     if (!strcmp(token, "$skin")) {
  778.       Cmd_Skin();
  779.       bspMem->skins[groupskin].numgroupskins++;
  780.     }
  781.     else if (!strcmp(token, "$skingroupend")) {
  782.       break;
  783.     }
  784.     else {
  785.       Error("$skin or $skingroupend expected\n");
  786.     }
  787.  
  788.   }
  789.  
  790.   if (bspMem->skins[groupskin].numgroupskins == 0)
  791.     Error("Empty group\n");
  792. }
  793.  
  794. /*
  795.  * ===============
  796.  * Cmd_FrameGroupStart  
  797.  * ===============
  798.  */
  799. void Cmd_FrameGroupStart(void)
  800. {
  801.   int groupframe;
  802.  
  803.   if (bspMem->numframes >= bspMem->max_numframes)
  804.     EpandClusters(bspMem, MODEL_FRAMES);
  805.   groupframe = bspMem->numframes++;
  806.  
  807.   bspMem->frames[groupframe].type = MDL_GROUP;
  808.   bspMem->frames[groupframe].numgroupframes = 0;
  809.  
  810.   while (1) {
  811.     GetToken(true);
  812.     if (endofscript)
  813.       Error("End of file during group");
  814.  
  815.     if (!strcmp(token, "$frame")) {
  816.       Cmd_Frame(1);
  817.     }
  818.     else if (!strcmp(token, "$framegroupend")) {
  819.       break;
  820.     }
  821.     else {
  822.       Error("$frame or $framegroupend expected\n");
  823.     }
  824.  
  825.   }
  826.  
  827.   bspMem->frames[groupframe].numgroupframes += bspMem->numframes - groupframe - 1;
  828.  
  829.   if (bspMem->frames[groupframe].numgroupframes == 0)
  830.     Error("Empty group\n");
  831. }
  832.  
  833. /*
  834.  * =================
  835.  * Cmd_Origin
  836.  * =================
  837.  */
  838. void Cmd_Origin(void)
  839. {
  840.   /*
  841.    * rotate points into frame of reference so model points down the positive x
  842.    * axis
  843.    */
  844.   GetToken(false);
  845.   adjust[1] = -atof(token);
  846.  
  847.   GetToken(false);
  848.   adjust[0] = atof(token);
  849.  
  850.   GetToken(false);
  851.   adjust[2] = -atof(token);
  852. }
  853.  
  854. /*
  855.  * =================
  856.  * Cmd_Eyeposition
  857.  * =================
  858.  */
  859. void Cmd_Eyeposition(void)
  860. {
  861.   /*
  862.    * rotate points into frame of reference so model points down the positive x
  863.    * axis 
  864.    */
  865.   GetToken(false);
  866.   model.eyeposition[1] = atof(token);
  867.  
  868.   GetToken(false);
  869.   model.eyeposition[0] = -atof(token);
  870.  
  871.   GetToken(false);
  872.   model.eyeposition[2] = atof(token);
  873. }
  874.  
  875. /*
  876.  * =================
  877.  * Cmd_ScaleUp
  878.  * =================
  879.  */
  880. void Cmd_ScaleUp(void)
  881. {
  882.   GetToken(false);
  883.   scale_up = atof(token);
  884. }
  885.  
  886. /*
  887.  * =================
  888.  * Cmd_Flags
  889.  * =================
  890.  */
  891. void Cmd_Flags(void)
  892. {
  893.   GetToken(false);
  894.   model.flags = atoi(token);
  895. }
  896.  
  897. /*
  898.  * =================
  899.  * Cmd_Modelname
  900.  * =================
  901.  */
  902. void Cmd_Modelname(void)
  903. {
  904.   WriteModel();
  905.   GetToken(false);
  906.   strcpy(outname, token);
  907. }
  908.  
  909. /*
  910.  * ===============
  911.  * ParseScript
  912.  * ===============
  913.  */
  914. void ParseScript(void)
  915. {
  916.   while (1) {
  917.     do {                            /* look for a line starting with a $ command */
  918.       GetToken(true);
  919.       if (endofscript)
  920.     return;
  921.       if (token[0] == '$')
  922.     break;
  923.       while (TokenAvailable())
  924.     GetToken(false);
  925.     } while (1);
  926.  
  927.     if (!strcmp(token, "$modelname")) {
  928.       Cmd_Modelname();
  929.     }
  930.     else if (!strcmp(token, "$base")) {
  931.       Cmd_Base();
  932.     }
  933.     else if (!strcmp(token, "$cd")) {
  934.       if (cdset)
  935.     Error("Two $cd in one model");
  936.       cdset = true;
  937.       GetToken(false);
  938.       strcpy(cdpartial, token);
  939.  
  940.       strcpy(cddir, ExpandPath(token));
  941.  
  942.     }
  943.     else if (!strcmp(token, "$sync")) {
  944.       model.synctype = ST_SYNC;
  945.     }
  946.     else if (!strcmp(token, "$origin")) {
  947.       Cmd_Origin();
  948.     }
  949.     else if (!strcmp(token, "$eyeposition")) {
  950.       Cmd_Eyeposition();
  951.     }
  952.     else if (!strcmp(token, "$scale")) {
  953.       Cmd_ScaleUp();
  954.     }
  955.     else if (!strcmp(token, "$flags")) {
  956.       Cmd_Flags();
  957.     }
  958.     else if (!strcmp(token, "$frame")) {
  959.       Cmd_Frame(0);
  960.     }
  961.     else if (!strcmp(token, "$skin")) {
  962.       Cmd_Skin();
  963.       model.numskins++;
  964.     }
  965.     else if (!strcmp(token, "$framegroupstart")) {
  966.       Cmd_FrameGroupStart();
  967.       model.numframes++;
  968.     }
  969.     else if (!strcmp(token, "$skingroupstart")) {
  970.       Cmd_SkinGroupStart();
  971.       model.numskins++;
  972.     }
  973.     else {
  974.       Error("bad command %s\n", token);
  975.     }
  976.   }
  977. }
  978.  
  979. /*
  980.  * ==============
  981.  * main
  982.  * ==============
  983.  */
  984. int main(int argc, char **argv)
  985. {
  986.   int i;
  987.   char path[1024];
  988.  
  989.   if (argc != 2 && argc != 4)
  990.     Error("usage: modelgen [-archive directory] file.qc");
  991.  
  992.   if (!strcmp(argv[1], "-archive")) {
  993.     archive = true;
  994.     strcpy(archivedir, argv[2]);
  995.     mprintf("Archiving source to: %s\n", archivedir);
  996.     i = 3;
  997.   }
  998.   else
  999.     i = 1;
  1000.  
  1001.   /*
  1002.    * load the script
  1003.    */
  1004.   strcpy(path, argv[i]);
  1005.  
  1006.   DefaultExtension(path, ".qc");
  1007.   SetQdirFromPath(path);
  1008.  
  1009.   LoadScriptFile(path);
  1010.  
  1011.   /*
  1012.    * parse it
  1013.    */
  1014.   __bzero(&model, sizeof(model));
  1015.  
  1016.   for (i = 0; i < 3; i++) {
  1017.     framesmins[i] = 9999999;
  1018.     framesmaxs[i] = -9999999;
  1019.   }
  1020.  
  1021.   ClearModel();
  1022.   strcpy(outname, argv[1]);
  1023.  
  1024.   ParseScript();
  1025.   WriteModel();
  1026.  
  1027.   return 0;
  1028. }
  1029.